/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package intopo.game.impl.ultimatum;

import intopo.game.Game;
import intopo.game.Game.Strategies;
import intopo.game.Strategy;
import intopo.util.Util;
import java.io.PrintStream;
import java.util.Scanner;

/**
 * Implementation of the <b>Ultimatum</b> game.  This is a two-player
 * sequential game.  The first player to move decides how to split a cake,
 * or a pile of money.  The second player to move can either accept or
 * refuse the division.  If he refuses no one gets anything.
 *
 * <p> There are two variation of this game.  One consists of a single
 * game, where the first player to move is defined by a random parameter.
 * In the second variation, both players can act as proponents and
 * therefore accept or refuse the division.  This consists in two games
 * played simultaneously.  With this assumption, we can use the strategies
 * implemented in this package.
 *
 * @author <a href="mailto:mariano.pedro@gmail.com">Pedro Mariano</a>
 *
 * @version 1.0 __DATE__
 */
final public class UltimatumGame
	implements Game<UltimatumStrategy>
{
	/**
	 * Constructor tag used in text streams and in the map ADT.
	 */
	static final String CONSTRUCTOR_TAG = "UltimatumGame";
	/**
	 * Variant of the ultimatum game to use.
	 */
	final Variant variant;
	/**
	 * Probability of the first player to act as proponent in the single game variant.
	 */
	final float probabilityFirstPlayerActProponent;
	/**
	 * Size of the cake.  This parameter is initialised from the value in
	 * field {@code Parameters.cakeSize}.
	 */
	final int cakeSize;
	/**
	 * 
	 * @param variant
	 * @param probabilityFirstPlayerActProponent
	 * @param cakeSize
	 */
	public UltimatumGame (Variant variant, float probabilityFirstPlayerActProponent, int cakeSize)
	{
		this.variant = variant;
		this.probabilityFirstPlayerActProponent = probabilityFirstPlayerActProponent;
		this.cakeSize = cakeSize;
		UltimatumStrategy.Parameters.cakeSize = this.cakeSize;
	}
	/**
	 *
	 * @param scanner
	 */
	public UltimatumGame (Scanner scanner)
	{
		this.variant = Variant.values () [scanner.nextInt ()];
		this.probabilityFirstPlayerActProponent = scanner.nextFloat ();
		this.cakeSize = scanner.nextInt ();
		UltimatumStrategy.Parameters.cakeSize = this.cakeSize;
	}
	/**
	 *
	 * @return
	 */
	public Variant getVariant ()
	{
		return this.variant;
	}
	/**
	 * 
	 * @return
	 */
	public float getProbabilityFirstPlayerActProponent ()
	{
		return this.probabilityFirstPlayerActProponent;
	}
	/**
	 * 
	 * @return
	 */
	public int getCakeSize ()
	{
		return this.cakeSize;
	}
	/**
	 * Returns {@code 2}, the number of players in this game.
	 * @return {@code 2}
	 */
	@Override
	public int numberPlayers ()
	{
		return 2;
	}
	/**
	 * Returns {@code 0}, the lowest payoff obtained in this game.  This
	 * payoff may result from the second player to move refusing the
	 */
	@Override
	public float lowestPayoff ()
	{
		return 0;
	}
	/**
	 * Returns the cake size, the highest payoff obtained in this game.
	 * This payoff may only be obtained when the proponent either keeps the
	 * cake or gives it all and the second player to move accepts the
	 * division.
	 *
	 * @return The cake size.
	 */
	@Override
	public float highestPayoff ()
	{
		switch (this.variant) {
		case SINGLE_GAME:
			return this.cakeSize;
		case SIMULTANEOUS_GAMES:
			return 2 * this.cakeSize;
		default:
			throw new UnsupportedOperationException ("Variant not supported yet: " + this.variant);
		}
	}
	/**
	 * Returns half the cake size.  We consider a fair division when a
	 * player has at least half the cake.
	 *
	 * @return half the cake size.
	 */
	@Override
	public float paretoPayoff ()
	{
		switch (this.variant) {
		case SINGLE_GAME:
			return this.cakeSize / 2f;
		case SIMULTANEOUS_GAMES:
			return this.cakeSize;
		default:
			throw new UnsupportedOperationException ("Variant not supported yet: " + this.variant);
		}
	}
	/**
	 * Play the Ultimatum game between the two players in {@code strategies}.
	 *
	 * <p><b>Pre-Condition</b> Field {@code strategies} has two strategies.
	 *
	 * @param strategies Strategies of the two players.
	 */
	@Override
	public void play (Strategies<UltimatumStrategy> strategies)
	{
		int cakeDivision;
		switch (this.variant) {
		case SINGLE_GAME:
			int move1, move2;
			if (Util.flipCoin (this.probabilityFirstPlayerActProponent)) {
				move1 = 0;
				move2 = 1;
			}
			else {
				move1 = 1;
				move2 = 0;
			}
			cakeDivision = strategies.getStrategy (move1).getDivisionProposal ();
			if (strategies.getStrategy (move2).isDivisionAcceptable (cakeDivision)) {
				strategies.setPayoff (move1, this.cakeSize - cakeDivision);
				strategies.setPayoff (move2, cakeDivision);
			}
			else {
				strategies.setPayoff (move1, 0f);
				strategies.setPayoff (move2, 0f);
			}
			return;
		case SIMULTANEOUS_GAMES:
			float p0 = 0f, p1 = 0f;
			cakeDivision = strategies.getStrategy (0).getDivisionProposal ();
			if (strategies.getStrategy (1).isDivisionAcceptable (cakeDivision)) {
				p0 += this.cakeSize - cakeDivision;
				p1 += cakeDivision;
			}
			cakeDivision = strategies.getStrategy (1).getDivisionProposal ();
			if (strategies.getStrategy (0).isDivisionAcceptable (cakeDivision)) {
				p0 += cakeDivision;
				p1 += this.cakeSize - cakeDivision;
			}
			strategies.setPayoff (0, p0);
			strategies.setPayoff (1, p1);
			return;
		default:
			throw new UnsupportedOperationException ("Variant not supported yet: " + this.variant);
		}
	}
	/**
	 *
	 * @param ps
	 */
	@Override
	public void write (PrintStream ps)
	{
		ps.print (UltimatumGame.CONSTRUCTOR_TAG);
		ps.print (' ');
		ps.print (this.variant.ordinal ());
		ps.print (' ');
		ps.print (this.probabilityFirstPlayerActProponent);
		ps.print (' ');
		ps.print (this.cakeSize);
	}
	/**
	 * Variations of the Ultimatum Game implemented in class {@code
	 * UltimatumGame}.  These variations also depend on the strategies
	 * implemented in this package.
	 */
	public enum Variant
	{
		/**
		 * Single game variant where only one game is played.  In this
		 * variant field {@code probabilityFirstPlayerActProponent} is used
		 * to determine the probability of the first player to act as
		 * proponent.
		 *
		 * <p> The first player is the first element in the collection passed
		 * to method {@code play(Strategies)}.
		 */
		SINGLE_GAME ("single"),
		/**
		 * Two simultaneous games where both players can act as proponents
		 * and as respondents to a proposal division.
		 *
		 * <p> In order to allow both players to play both roles and to use
		 * the strategies implemented in this package, the games are
		 * considered simultaneous.  These strategies do not take into
		 * account history of past games.
		 */
		SIMULTANEOUS_GAMES ("simultaneous");
		/**
		 *
		 */
		private String string;
		/**
		 *
		 * @param string
		 */
		private Variant (String string)
		{
			this.string = string;
		}
		/**
		 * 
		 */
		public String toString ()
		{
			return this.string;
		}
	}
}

// Local Variables:
// mode: java
// mode: flyspell-prog
// ispell-local-dictionary: "british"
// End:
